package org.smartboot.ioc.transport;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.smartboot.ioc.MessageProcessor;
import org.smartboot.ioc.Protocol;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;

/**
 * @version NioQuickClient.java, v 0.1 2015年3月20日 下午2:55:08 Seer Exp.
 * @author三刀
 */
public class NioQuickClient<T> implements Runnable {
    /**
     * Socket连接锁,用于监听连接超时
     */
    private final Object connectLock = new Object();
    private Logger logger = LoggerFactory.getLogger(NioQuickClient.class);
    /**
     * 服务配置
     */
    private IoServerConfig<T> config = new IoServerConfig<T>();
    private Selector selector;
    /**
     * 传输层Channel服务处理线程
     */
    private Thread serverThread;
    /**
     * 客户端会话信息
     */
    private NioSession<T> session;

    private SocketChannel socketChannel;


    /*
     * (non-Javadoc)
     *
     * @see java.lang.Runnable#run()
     */
    public final void run() {
        try {
            while (socketChannel.isOpen()) {
                // 优先获取SelectionKey,若无关注事件触发则阻塞在selector.select(),减少select被调用次数
                Set<SelectionKey> keySet = selector.selectedKeys();
                if (keySet.isEmpty()) {
                    selector.select();
                }
                Iterator<SelectionKey> keyIterator = keySet.iterator();
                // 执行本次已触发待处理的事件
                while (keyIterator.hasNext()) {
                    SelectionKey key = keyIterator.next();
                    // 读取客户端数据
                    if (key.isReadable()) {
                        key.interestOps(key.interestOps() & ~SelectionKey.OP_READ);
                        AsynchronousSocketChannel asynchronousSocketChannel = (AsynchronousSocketChannel) key.attachment();
                        asynchronousSocketChannel.doRead();
                    } else if (key.isWritable()) {// 输出数据至客户端
                        key.interestOps(key.interestOps() & ~SelectionKey.OP_WRITE);
                        AsynchronousSocketChannel asynchronousSocketChannel = (AsynchronousSocketChannel) key.attachment();
                        asynchronousSocketChannel.doWrite();
                    } else if (key.isConnectable()) {// 建立新连接,Client触发Connect,Server触发Accept
                        acceptConnect(key);
                    } else {
                        logger.warn("奇怪了...");
                    }
                    // 移除已处理的事件
                    keyIterator.remove();
                }
            }
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
            shutdown();
        }
        logger.info("Channel is stop!");
    }

    /**
     * 接受并建立客户端与服务端的连接
     *
     * @param key
     * @throws IOException
     */
    private void acceptConnect(SelectionKey key) throws IOException {
        SocketChannel channel = (SocketChannel) key.channel();
        channel.finishConnect();
        key.interestOps(key.interestOps() & ~SelectionKey.OP_CONNECT | SelectionKey.OP_READ);
        AsynchronousSocketChannel socketChannel = new AsynchronousSocketChannel(key);
        session = new NioSession<T>(socketChannel, config, new ReadCompletionHandler(), new WriteCompletionHandler(), false);
        session.initSession();
        logger.info("success connect to " + channel.socket().getRemoteSocketAddress().toString());
        key.attach(socketChannel);
        synchronized (connectLock) {
            connectLock.notifyAll();
        }
    }


    /*
     * (non-Javadoc)
     *
     * @see net.vinote.smart.socket.transport.ChannelService#shutdown()
     */
    public final void shutdown() {
        try {
            selector.close();
            selector.wakeup();
        } catch (final IOException e) {
            logger.warn(e.getMessage(), e);
        }
        try {
            socketChannel.close();
        } catch (final IOException e) {
            logger.warn(e.getMessage(), e);
        }
    }

    /*
     * (non-Javadoc)
     *
     * @see net.vinote.smart.socket.transport.ChannelService#start()
     */
    public final void start() {
        try {
            selector = Selector.open();
            socketChannel = SocketChannel.open();
            socketChannel.configureBlocking(false);
            socketChannel.register(selector, SelectionKey.OP_CONNECT);
            socketChannel.connect(new InetSocketAddress(config.getHost(), config.getPort()));
            serverThread = new Thread(this, "QuickClient-" + hashCode());
            serverThread.start();
//            socketChannel.socket().setSoTimeout(config.getTimeout());

            if (session != null) {
                return;
            }
            synchronized (connectLock) {
                if (session != null) {
                    return;
                }
                try {
                    connectLock.wait(1000);
                } catch (final InterruptedException e) {
                    logger.warn("", e);
                }
            }

        } catch (final IOException e) {
            logger.warn("", e);
        }
    }


    /**
     * 设置远程连接的地址、端口
     *
     * @param host
     * @param port
     * @return
     */
    public NioQuickClient<T> connect(String host, int port) {
        this.config.setHost(host);
        this.config.setPort(port);
        return this;
    }

    /**
     * 设置协议对象
     *
     * @param protocol
     * @return
     */
    public NioQuickClient<T> setProtocol(Protocol<T> protocol) {
        this.config.setProtocol(protocol);
        return this;
    }

    /**
     * 设置消息处理器
     *
     * @param processor
     * @return
     */
    public NioQuickClient<T> setProcessor(MessageProcessor<T> processor) {
        this.config.setProcessor(processor);
        return this;
    }

}
